﻿'====================================================================
' ConverterDialog
'--------------------------------------------------------------------
' The dialog form for the menu item Converter.
'
' It converts a plain text log file, to a XML based.
'
' The user selects one of the supported log file types and the actual
' log file from her computer. Then, by clicking the button "Convert",
' it will be generated a new log file in XML format. The new file can
' be used as input to the main program.
'====================================================================

Imports System.Windows.Forms
Imports System.IO
Imports System.Text.RegularExpressions

Public Class ConverterDialog

   Dim msLogFile As String = ""
   Dim msConvertedLogFile As String = ""

   ' Group the needed arguments in one object
   Public Structure ConvertedLogFileArguments
      Public sRegex As String
      Public sXmlTags() As String

      Public Sub New(ByVal _sRegex As String, ByVal _sXmlTags() As String)
         sRegex = _sRegex
         sXmlTags = _sXmlTags
      End Sub
   End Structure

   ' Replaces the characters that have special meaning in XML with entity references
   Private Sub ReplaceWithEntityReferences(ByRef sText As String)
      sText = Replace(sText, "&", "&amp;")
      sText = Replace(sText, "<", "&lt;")
      sText = Replace(sText, ">", "&gt;")
      sText = Replace(sText, "'", "&apos;")
      sText = Replace(sText, """", "&quot;")
   End Sub

   ' Core function for converting an Apache httpd-access log file to XML format
   Private Function ConvertHttpdAccessLog(ByVal sRegex As String, ByVal sXmlTags() As String) As String
      Dim reRegex As Regex = New Regex(sRegex)
      Dim srReader As StreamReader = New StreamReader(msLogFile)
      Dim sTextLine As String
      Dim sValue As String
      Dim mtMatch As Match
      Dim sConverted As String

      sConverted = "<ApacheHttpdAccessLog>" & vbNewLine
      Do While srReader.Peek() <> -1
         sTextLine = srReader.ReadLine()
         mtMatch = reRegex.Match(sTextLine)
         If mtMatch.Success Then
            sConverted = sConverted & "<Visit>" & vbNewLine
            For group = 1 To mtMatch.Groups.Count - 1
               sConverted = sConverted & "<" & sXmlTags(group - 1) & ">" & vbNewLine
               sValue = mtMatch.Groups(group).Value
               ReplaceWithEntityReferences(sValue)
               sConverted = sConverted & sValue & vbNewLine
               sConverted = sConverted & "</" & sXmlTags(group - 1) & ">" & vbNewLine
            Next
            sConverted = sConverted & "</Visit>" & vbNewLine
         Else
            Return ""
         End If
      Loop
      sConverted = sConverted & "</ApacheHttpdAccessLog>"

      Return sConverted
   End Function

   Private Sub lbChooseType_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lbChooseType.SelectedIndexChanged
      If msLogFile.Length <> 0 Then
         btnConvert.Enabled = True
      End If
   End Sub

   Private Sub btnOpenLogFile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenLogFile.Click
      ofdOpenLogFile.Title = "Open a Log File"
      ofdOpenLogFile.Filter = "Log File|*.log"
      If ofdOpenLogFile.ShowDialog() = DialogResult.OK Then
         msLogFile = ofdOpenLogFile.FileName
         If lbChooseType.SelectedIndex <> -1 Then
            btnConvert.Enabled = True
         End If
      End If
   End Sub

   Private Sub btnConvert_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConvert.Click
      pbConverting.Style = ProgressBarStyle.Marquee
      Select Case lbChooseType.SelectedIndex
         Case 0
            Dim sRegex As String = "^([0-9.]+) ([^ ]+) ([^ ]+) (\[.+\]) (""[^""]+"") ([0-9]{3}) ([0-9-]+)$"
            Dim sXmlTags() As String = {"IPAddress", "ClientIdentity", "UserID", "DateTime", "Request", "StatusCode", "Size"}
            Dim strArgs As ConvertedLogFileArguments = New ConvertedLogFileArguments(sRegex, sXmlTags)
            bwConvert.RunWorkerAsync(strArgs)
         Case 1
            Dim sRegex As String = "^([0-9.]+) ([^ ]+) ([^ ]+) (\[.+\]) (""[^""]+"") ([0-9]{3}) ([0-9-]+) (""[^""]+"") (""[^""]+"")$"
            Dim sXmlTags() As String = {"IPAddress", "ClientIdentity", "UserID", "DateTime", "Request", "StatusCode", "Size", "Referer", "UserAgent"}
            Dim strArgs As ConvertedLogFileArguments = New ConvertedLogFileArguments(sRegex, sXmlTags)
            bwConvert.RunWorkerAsync(strArgs)
      End Select
   End Sub

   ' Use of BackgroundWorker because the intensive operation of converting to XML blocked the UI thread
   Private Sub bwConvert_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwConvert.DoWork
      Dim strArgs As ConvertedLogFileArguments = e.Argument
      msConvertedLogFile = ConvertHttpdAccessLog(strArgs.sRegex, strArgs.sXmlTags)

      If bwConvert.CancellationPending = True Then
         e.Cancel = True
      End If
   End Sub

   Private Sub bwConvert_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bwConvert.RunWorkerCompleted
      pbConverting.Style = ProgressBarStyle.Blocks

      If e.Cancelled Then
         Exit Sub
      End If

      sfdConvertedLogFile.Title = "Save Converted Log File"
      sfdConvertedLogFile.Filter = "XML File|*.xml"
      If msConvertedLogFile.Length = 0 Then
         MsgBox("Malformed log file or it does not match with the selected type.", MsgBoxStyle.Exclamation, "Converter")
      Else
         If sfdConvertedLogFile.ShowDialog() = Windows.Forms.DialogResult.OK Then
            My.Computer.FileSystem.WriteAllText(sfdConvertedLogFile.FileName, msConvertedLogFile, False)
         End If
      End If
   End Sub

   Private Sub btnOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOk.Click
      Me.DialogResult = System.Windows.Forms.DialogResult.OK
      Me.Close()
   End Sub

   Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
      bwConvert.CancelAsync()
      Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
      Me.Close()
   End Sub

End Class
